package com.exploadingelephants.views;

import java.util.LinkedList;
import java.util.List;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Point;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import ch.exploadingelephants.graph.MachineGraph;
import ch.exploadingelephants.model.ConveyorBeltItem;
import ch.exploadingelephants.model.ConveyorDirection;
import ch.exploadingelephants.model.Grid;
import ch.exploadingelephants.model.Machine;
import ch.exploadingelephants.model.MachineItem;
import ch.exploadingelephants.model.Point2D;

import com.exploadingelephants.R;

/**
 * @author Sandro De Zanet
 * 
 */
public class MachineView extends SurfaceView implements SurfaceHolder.Callback {

    private final Bitmap field;
    private final List<Point2D> selection = new LinkedList<Point2D>();

    final int M = 5;
    final int N = 5;
    private final int fieldWidth;
    private final int fieldHeight;

    private boolean pressing = false;
    private final Machine machine;
    private MachineItem activeItem;
    private final Grid<List<Bitmap>> bitmapGrid;

    private AnimatedMachineThread thread;
    private int tick;
    private boolean editable;

    /**
     * @param context
     * @param machine
     */
    public MachineView(Context context, Machine machine) {
	super(context);

	getHolder().addCallback(this);

	// make the GamePanel focusable so it can handle events
	setFocusable(true);
	tick = 0;

	// setWillNotDraw(false);
	field = BitmapFactory.decodeResource(getResources(), R.drawable.empty);
	fieldWidth = field.getWidth();
	fieldHeight = field.getHeight();
	if (machine != null) {
	    this.machine = machine;
	} else {
	    this.machine = new Machine(M, N);
	}
	bitmapGrid = gridFromMachine(this.machine);
	activeItem = new ConveyorBeltItem(ConveyorDirection.NORTH);
	editable = true;
    }

    /**
     * @param machine2
     * @return
     */
    private Grid<List<Bitmap>> gridFromMachine(Machine machine) {
	Grid<List<Bitmap>> bmGrid = new Grid<List<Bitmap>>(machine.getWidth(),
		machine.getHeight());
	for (int i = 0; i < machine.getWidth(); i++) {
	    for (int j = 0; j < machine.getHeight(); j++) {
		if (machine.get(i, j) != null) {
		    bmGrid.set(i, j,
			    machine.get(i, j).getBitmaps(getResources()));
		}
	    }
	}
	return bmGrid;
    }

    /*
     * (non-Javadoc)
     * 
     * @see android.view.View#onTouchEvent(android.view.MotionEvent)
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
	if (!editable) {
	    return true;
	}
	boolean before = pressing;
	pressing = (pressing || event.getAction() == MotionEvent.ACTION_DOWN)
		&& (event.getAction() != MotionEvent.ACTION_UP);

	int touchX = (int) event.getX();
	int touchY = (int) event.getY();
	Point2D point = new Point2D(touchX / fieldWidth, touchY / fieldHeight);

	if (machine.inLimits(point)) {

	    MachineItem existingItem = machine.get(point);
	    if (existingItem == null || existingItem.isDeletable()) {
		if (selection.isEmpty()) {
		    selection.add(point);

		} else if (!selection.get(selection.size() - 1).equals(point)) {
		    selection.add(point);

		}
		if (!before) {
		    selection.clear();// reset the selection
		}
		if (event.getAction() == MotionEvent.ACTION_UP) {
		    if (selection.size() == 1) {
			setItem(selection.get(0).x,
				selection.get(0).y,
				activeItem == null ? null : activeItem
					.copyItem());
		    }
		    Point diff = null;
		    for (int i = 0; i < selection.size(); i++) {
			Point2D current = selection.get(i);
			Point2D next = current;
			if (i < selection.size() - 1) {
			    next = selection.get(i + 1);
			    diff = new Point(next.x - current.x, next.y
				    - current.y);
			}
			if (diff != null) {
			    setItem(current.x,
				    current.y,
				    activeItem == null ? null : this.activeItem
					    .copyItem());
			}
		    }
		}
		// invalidate();
	    }
	}
	return true;
    }

    /**
     * @param x
     * @param y
     * @param machineItem
     */
    private void setItem(int x, int y, MachineItem machineItem) {
	machine.set(x, y, machineItem);
	if (machineItem != null) {
	    bitmapGrid.set(x, y, machineItem.getBitmaps(getResources()));
	} else {
	    bitmapGrid.set(x, y, null);
	}
    }

    /*
     * (non-Javadoc)
     * 
     * @see android.view.View#onMeasure(int, int)
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
	setMeasuredDimension(this.fieldWidth * machine.getWidth(),
		this.fieldHeight * machine.getHeight());
    }

    /**
     * @param item
     */
    public void setActiveItem(MachineItem item) {
	this.activeItem = item;
    }

    /**
     * @return the machine
     */
    public Machine getMachine() {
	return machine;
    }

    /**
     * @return the generated machine graph
     */
    public MachineGraph getMachineGraph() {
	return machine.generateMachineGraph();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * android.view.SurfaceHolder.Callback#surfaceChanged(android.view.SurfaceHolder
     * , int, int, int)
     */
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
	    int height) {
	// do nothing
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * android.view.SurfaceHolder.Callback#surfaceCreated(android.view.SurfaceHolder
     * )
     */
    public void surfaceCreated(SurfaceHolder holder) {
	thread = new AnimatedMachineThread(getHolder(), this);
	thread.setRunning(true);
	thread.start();
    }

    /*
     * (non-Javadoc)
     * 
     * @see android.view.SurfaceHolder.Callback#surfaceDestroyed(android.view.
     * SurfaceHolder)
     */
    public void surfaceDestroyed(SurfaceHolder holder) {
	stop();
    }

    public void stop() {
	boolean retry = true;
	thread.setRunning(false);

	while (retry) {
	    // We will try until the main game loop will be stopped
	    try {
		thread.join();
		retry = false;
	    } catch (InterruptedException e) {
	    }
	}
	thread = null;
    }

    /**
     * @param canvas
     */
    public void render(Canvas canvas) {
	if (canvas != null) {
	    canvas.drawColor(Color.BLACK);
	    for (int i = 0; i < machine.getWidth(); i++) {
		for (int j = 0; j < machine.getHeight(); j++) {
		    if (machine.get(i, j) != null) {
			List<Bitmap> list = bitmapGrid.get(i, j);
			if (list != null) {
			    Bitmap bitmap = list.get(tick % list.size());
			    if (bitmap != null) {
				canvas.drawBitmap(bitmap, i * fieldWidth, j
					* fieldHeight, null);
			    }
			}
		    } else {
			if (field != null) {
			    canvas.drawBitmap(field, i * fieldWidth, j
				    * fieldHeight, null);
			}
		    }
		}
	    }
	    tick++;
	}
    }

    /**
     * This is the game update method. It iterates through all the objects and
     * calls their update method if they have one or calls specific engine's
     * update method.
     */
    public void update() {
    }

    /**
     * @param b
     */
    public void setEditable(boolean b) {
	this.editable = false;
    }

    /**
     * 
     */
    public void simulateRobot() {
	// TODO Auto-generated method stub

    }
}
